package io.github.zerobyteword.metricspringstartup;

import org.springframework.core.metrics.StartupStep;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class MetricStartupStep implements StartupStep {
    private String name;
    private long id;
    private long start;
    private long end;

    private final List<Tag> tags = new CopyOnWriteArrayList<>();

    private MetricStartupStep parent;

    private Consumer<MetricStartupStep> consumer;

    private volatile AtomicBoolean isEnded = new AtomicBoolean(false);

    public MetricStartupStep(MetricStartupStep parent, String name, long id, long start, Consumer<MetricStartupStep> consumer) {
        this.parent = parent;
        this.name = name;
        this.id = id;
        this.start = start;
        this.consumer = consumer;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public long getId() {
        return this.id;
    }

    @Override
    public Long getParentId() {
        MetricStartupStep temp = this.parent;
        return temp == null ? null : temp.getId();
    }

    @Override
    public StartupStep tag(String key, String value) {
        this.tags.add(new StepTag(key, value));
        return this;
    }

    @Override
    public StartupStep tag(String key, Supplier<String> value) {
        this.tag(key, value.get());
        return this;
    }

    @Override
    public Tags getTags() {
        return Collections.unmodifiableList(this.tags)::iterator;
    }

    @Override
    public void end() {
        Consumer<MetricStartupStep> temp = this.consumer;
        if (temp != null && this.isEnded.compareAndSet(false, true)) {
            this.end = System.currentTimeMillis();
            temp.accept(this);
        }
    }

    public boolean isEnded() {
        return isEnded.get();
    }

    public long costTime() {
        return end - start;
    }

    public MetricStartupStep getParent() {
        return this.parent;
    }


    static class StepTag implements Tag {

        private final String key;

        private final String value;

        public StepTag(String key, String value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public String getKey() {
            return this.key;
        }

        @Override
        public String getValue() {
            return this.value;
        }
    }
}
